#!/usr/bin/perl

use strict;
use Data::Dumper;
use sigtrap 'handler', \&close_network, 'normal-signals';

my $BASE_DIR="/home/ipye/metaring/MetaThrift";
my $MDS_CMD=$BASE_DIR."/bin/MetaThrift";
my $CLIENT_CMD=$BASE_DIR."/bin/RingerFS";
my $PORT=9010;
my $UMOUNT_CMD="fusermount -u";

my %assinged_servers=();
my %fake2real=();
my @servers=();
my @clients=();
my @mds_pids=();
my @client_pids=();
my %remote_pids=();
my %remote_client_pids=();

my $last_port = $PORT;
my $client_db_name = "client_db.db";
my $mds_db_name = "mds_db.db";
my $db_env = $BASE_DIR."/db/db_mds";
my $ringer_mnt_point=$BASE_DIR."/mnt";
my $client_logger=$BASE_DIR."/logging_config";
my $mds_logger=$BASE_DIR."/mds_logging_config";
my $local_cache_dir="/tmp/ringer_l_cache_1";
my $trust_file=$BASE_DIR."/certs/combined_server_ca.pem";
my $public_key=$BASE_DIR."/certs/combined_server_ca.pem";
my $private_key=$BASE_DIR."/certs/combined_server_ca.pem";

## how we close the network down
sub close_network {
  my($sig) = @_;
  print "Caught a SIG$sig--shutting down\n";

  for my $remote (sort {$remote_client_pids{$b}[2] <=> $remote_client_pids{$a}[2]} keys %remote_client_pids){
    my $rem = $remote_client_pids{$remote};
    print "Stopping Client on ".@$rem[0].", pid ".@$rem[1]."\n";
    `ssh @$rem[0] $UMOUNT_CMD @$rem[3]`
  }
  sleep 1;

  for my $remote (sort {$remote_pids{$b}[2] <=> $remote_pids{$a}[2]} keys %remote_pids){
    my $rem = $remote_pids{$remote};
    print "Stopping MDS on ".@$rem[0].", pid ".@$rem[1]."\n";
    `ssh @$rem[0] kill -s $sig  @$rem[1]`
  }
  sleep 1;
  exit(0);
}


## Parse the servers descriptor file.
open(FH, $ARGV[0]) or die "Can't open servers desc.";
while (<FH>){
  chomp;
  push @servers, $_;
}
close FH;

## Parse the network descriptor file.
open(FH, $ARGV[1]) or die "Can't open network desc.";
while (<FH>){
  if ($_ =~ /^(\s*)MDS/ && $_ =~ /^(.*?) +?(.*)$/){

    my $mds = $1;
    my @parents = split (/,/, $2);

    ## Assing the MDS a real server and port.
    my $next_server = shift @servers;
    if (!$assinged_servers{$next_server}){
      $fake2real{$mds} = [$next_server, $PORT, []];
      $assinged_servers{$next_server} = {$mds => $PORT};
    } else {
      my $offset = scalar (keys %{$assinged_servers{$next_server}});
      $assinged_servers{$next_server}{$mds} = $PORT+$offset;
      $fake2real{$mds} = [$next_server, $PORT+$offset, []];
    }
    for my $parent (@parents){
      if ($parent =~ /^(\s*)(\S*)(\s*)$/){
	push @{$fake2real{$mds}[2]}, $2;
      }
    }
    push @servers, $next_server;
  } elsif ($_ =~ /^(\s*)CLIENT( +?)(.*)$/) {
    
    my $mds = $3;
    my $next_client = scalar @clients;

    ## Assing the Client a real server and port.
    my $next_server = shift @servers;
   
    my $offset = scalar (keys %{$assinged_servers{$next_server}});
    $assinged_servers{$next_server}{"CLIENT".$next_client} = $PORT+$offset;

    my $mds_host = $fake2real{$mds}[0];
    my $mds_port = $fake2real{$mds}[1];
    
    push @clients, [$next_server, $PORT+$offset, $mds_host, $mds_port];
    push @servers, $next_server;
  }
}
close FH;

#print Dumper(\%assinged_servers);
#print Dumper(\%fake2real);
#print Dumper(\@clients);

## deploy the MDS
my $killOrder = 0;
for my $mds (sort keys %fake2real){
  my $parents = "";
  for my $parent (@{$fake2real{$mds}[2]}){
    if ($fake2real{$parent}[0]){
      $parents .= "--parent ".$fake2real{$parent}[0]." --parent_port ".$fake2real{$parent}[1]." ";
    }
  }
  my @args = ("ssh", "-o StrictHostKeyChecking=no", $fake2real{$mds}[0], "$MDS_CMD --host_name ".$fake2real{$mds}[0]." --port ".$fake2real{$mds}[1]." --db_name $mds_db_name --trust_file $trust_file --public_key $public_key --private_key $private_key --logger $mds_logger --db_env $db_env $parents");

  my $pid = fork();
  die "fork() failed: $!" unless defined $pid;
  if ($pid) { ## parent
    push @mds_pids, $pid;
    sleep 1;
    my @poss_pids = split /\n/, `ssh -o StrictHostKeyChecking=no $fake2real{$mds}[0] ps -f -A | grep $MDS_CMD`;
    for my $poss_pid (@poss_pids){
      if ($poss_pid =~ /^\w*\s*(\d+)/){
	if (!$remote_pids{$fake2real{$mds}[0]."$1"}){
	  print "remote PID: $1\n";
	  $remote_pids{$fake2real{$mds}[0]."$1"} = [$fake2real{$mds}[0], $1, $killOrder];
	  last;
	}    
      }
    }
  } else { ## child
    system(@args) == 0
      or die "system @args failed: $?"
  }
  $killOrder++;
}

## and deploy the clients
for my $client (@clients){
  my @args = ("ssh", "-o StrictHostKeyChecking=no", @$client[0], "$CLIENT_CMD --fileserver_bind_address ".@$client[0]." --fileserver_bind_port ".@$client[1]." --local_cache_dir $local_cache_dir --trust_file $trust_file --public_key $public_key --private_key $private_key --mds_address ".@$client[2]." --mds_port ".@$client[3]." --logger $mds_logger $ringer_mnt_point -o nonempty &");

  my $pid = fork();
  die "fork() failed: $!" unless defined $pid;
  if ($pid) { ## parent
    push @client_pids, $pid;
    sleep 1;
    my @poss_pids = split /\n/, `ssh -o StrictHostKeyChecking=no @$client[0] ps -f -A | grep $CLIENT_CMD`;
    for my $poss_pid (@poss_pids){
      if ($poss_pid =~ /^\w*\s*(\d+)/){
	if (!$remote_client_pids{@$client[0]."$1"}){
	  print "remote PID: $1\n";
	  $remote_client_pids{@$client[0]."$1"} = [@$client[0], $1, $killOrder, $ringer_mnt_point];
	  last;
	}    
      }
    }
  } else { ## child
    system(@args) == 0
      or die "system @args failed: $?"
  }
  $killOrder++;
}

## now, wait for the command to shut the network down.
while (1){
  sleep 1;
}
